using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Collections;
using System.Windows.Forms;
using Camalon.Drawing.Shapes;

namespace Camalon.Drawing
{
	/// <summary>
	/// A ShapeContainer contains a collection of shapes.  This container uses
	/// two designers:  a root designer that allows the container to be a top
	/// level component where shapes can be visually added, and a normal designer
	/// that is used when the shape container is added to another object like
	/// a form.
	/// 
	/// ShapeContainer also defines a custom serializer.  This serializer is used
	/// to generate code for an "adapter" class that can be used to fit a shape
	/// to a Windows Forms paint event.
	/// 
	/// Finally, placing a ShapeContainer on another shape container is of
	/// limited value, while placing it on other types of designers is 
	/// what we intend.  So, we override the toolbox item filter that sits
	/// on our base Shape class.
	/// </summary>
	[ToolboxItem(false)]
    [Designer("Camalon.Drawing.Design.ShapeContainerRootDesigner, Camalon.Drawing.Design", typeof(IRootDesigner))]
    [Designer("Camalon.Drawing.Design.ShapeContainerDesigner, Camalon.Drawing.Design", typeof(IDesigner))]
    public class ShapeContainer : Shape, ISupportInitialize, IShapeContainerProvider
	{
        /// <summary>
        /// A ShapeContainer contains a collection of shapes.  This container uses
        /// two designers:  a root designer that allows the container to be a top
        /// level component where shapes can be visually added, and a normal designer
        /// that is used when the shape container is added to another object like
        /// a form.
        /// 
        /// ShapeContainer also defines a custom serializer.  This serializer is used
        /// to generate code for an "adapter" class that can be used to fit a shape
        /// to a Windows Forms paint event.
        /// 
        /// Finally, placing a ShapeContainer on another shape container is of
        /// limited value, while placing it on other types of designers is 
        /// what we intend.  So, we override the toolbox item filter that sits
        /// on our base Shape class.
        /// </summary>

		#region IShapeContainer Members

		Camalon.Drawing.ShapeContainer Camalon.Drawing.IShapeContainerProvider.ShapeContainer
		{
			get
			{
				// TODO:  Add ShapeContainer.Camalon.Drawing.IShapeContainer.ShapeContainer getter implementation
				return this;
			}
		}

		#endregion

		#region Events

		[Category("Layout")]
		public event EventHandler		ScalingResizeChanged		= null;

		[Category("Region")]
		public event EventHandler		ClipChanged		= null;

		#endregion Events

		ShapeCollection _shapes				= new ShapeCollection();
		
		bool			_initializing		= true;
		RectangleF		_initialBounds		= RectangleF.Empty;
		Hashtable		_margins			= new Hashtable(); 


//		private		bool		_excludeGlobal			= true;//Taken out 10.11.2006
		private		bool		_clip		= false;

		public ShapeContainer():this(new Rectangle(0, 0,400,400))
		{

		}

		public ShapeContainer( Rectangle bounds ):base(bounds)//New
		{
			try
			{
				base.SetCanOutline( false );
				this.BeginInit();

				this.MinSize = new Size( 50,50);
				//IMPORTANT ! --------------------------------------------------------------
				_lockedBounds = bounds;
				//--------------------------------------------------------------------------
				this.Shapes.CollectionChanged+=new ShapeCollectionEventHandler(OnShapeCollectionChanged);
				
				this.EndInit();


			}
			catch(Exception e)
			{
				FileWatcher.WriteLine( "ShapeContainer::ctor, " + e.StackTrace );
			}


		}




		protected override void Dispose(bool disposing)
		{
			this.Shapes.CollectionChanged-=new ShapeCollectionEventHandler(OnShapeCollectionChanged);

			foreach( Shape shape in _shapes) 
				if( shape != null)	{ shape.Parent = null; RemoveShapeEvents( shape );	}

		
			_shapes.Clear();
			_shapes.Dispose();

			base.Dispose (disposing);
		}


		#region ISupportInitialize Members


		private bool IsChildContainer
		{
			get
			{
				return (this.Status & 2) > 0; // Check 2nd Bit set
			}
		}

		private bool IsPublic( Shape shape )
		{
			return (shape.Status & 1) > 0; // Check 1st Bit set
		}



		[Browsable( false)]
		public bool Initializing
		{
			get{ return _initializing;}
		}

	
		public void BeginInit()
		{


			this.SuspendLayout();
			//Do also this....
			foreach( Shape shape in _shapes)
			{
				shape.SuspendLayout();
				if( shape.Parent != this) shape.Parent = this;
			}


			_initialBounds	= this.Bounds;
			
			//During undo redo and during the runtime, the container is null.
			//Only while rebuilding container is not null.
			//That means, during undo and redo, this.Bounds is the same as the value 
			//at the end of this EndInit. Because of this:

			_initializing	= true;


		}


		public void EndInit()
		{
			try
			{


				this.SuspendLayout();
				foreach( Shape shape in _shapes) { shape.SuspendLayout(); RemoveShapeEvents( shape );	}



				RefreshResizeTransformBase();
				CleanContainerBaseShape();


				//TODO:Commented ShapeGroup code block. Reserved for future use
				//Remove shape from shapes if already in a group
				//--------------------------------------------------------
				#region Commented ShapeGroup code
				//				ArrayList shapeGroups = new ArrayList();
				//				foreach( Shape shape in _shapes)
				//					if( shape is ShapeGroup) {shapeGroups.Add( shape);}
				//			
				//				if( shapeGroups.Count > 0)
				//					for(int i = 0; i < _shapes.Count; i++)
				//					{
				//						Shape shape = _shapes[i];
				//						foreach( ShapeGroup group in shapeGroups)
				//							if( group.Shapes.Contains( shape ))
				//							{
				//								_shapes.Remove(shape);
				//								shape.Parent = group;
				//								i--; break;
				//							}
				//					}
				#endregion

				//Relayout shapes
				//--------------------------------------------------------------
				foreach( Shape shape in _shapes)
				{
					bool newItem = false;
					if( !_margins.ContainsKey( shape))
					{
						_margins.Add( shape, GetMargins( shape));
						newItem = true;
					}


					if(!newItem )
						if( !IsPublic(shape) || (this.IsChildContainer && IsPublic(shape)) )
							ResetLayout(_initialBounds, this.Bounds,shape);
				
					_margins[ shape ] = GetMargins( shape ); // Read always again !!!!
					

					if( newItem ) continue;

					//Note:Shape site is null, when not public
//					if(shape.Site!= null && !IsPublic(shape) )	shape.Status |= 1;//Excluded 09.02.2007 !!!! Test it..

				}
					
			}
			catch(Exception e)
			{
				FileWatcher.WriteLine( "ShapeContainer::EndInit, " + e.StackTrace );
			}
			finally
			{
				// set init complete
				_initializing = false;
				foreach( Shape shape in _shapes) { shape.ResumeLayout(); AddShapeEvents( shape );}
				this.ResumeLayout();
				InvokeInternalChanged( false );
				
			}
		

		}

		#endregion


		#region Event Handlers
		protected virtual void OnScalingResizeChanged(EventArgs eventArg)
		{
			if( this.ScalingResizeChanged != null) this.ScalingResizeChanged( this, eventArg );
		}

		protected virtual void OnClipChanged(EventArgs eventArg)
		{
			if( this.ClipChanged != null) this.ClipChanged( this, eventArg );
		}

		#endregion Event Handlers

		#region Properties

		internal bool ShouldScaleOnResize
		{
			get
			{
				return (_scalingResize && this.IsChildContainer);
			}
		}

		

		private		bool		_scalingResize				= false;
		private		RectangleF  _lockedBounds				= Rectangle.Empty;

		protected bool ShouldSerializeScalingResizeBase()
		{
			return _scalingResize; 
		}

		[Category("Transformation")]
		[Browsable( false)]
		public Size ScalingResizeBase
		{
			get{ return Rectangle.Round(_lockedBounds).Size;}
			set
			{
				_lockedBounds.Size = value;	
				_lockedBounds.Location = _initialBounds.Location;
				RefreshResizeTransformBase();
				ResetLayout( _initialBounds, _lockedBounds);
			}
		}


		[Category("Transformation")]
		[DefaultValue(false)]
		[RefreshProperties( RefreshProperties.All)]
		public virtual bool ScalingResize
		{
			get{ return _scalingResize;}
			set
			{

				if( _scalingResize == value) return;


				//While resizing scale warping is not allowed
				//---------------------------------------------
				if( value && this.Warp != Warp.Empty) this.Warp = Warp.Empty;	
				RewarpChildren( null) ;
				base.SetCanWarp( !value );
				base.SetCanScale(!value);


				//---------------------------------------------
				if( value )
				{
					_lockedBounds.Location = this.Position;
					if( this.DesignMode && !_initializing) _lockedBounds.Size = this.Size;
					_scalingResize = value;

				}
				else
				{

					this.Size = Size.Round( _lockedBounds.Size );
					_scalingResize = value;
					
					//Reset margins
					foreach( Shape shape in _shapes)
					{
						if( !_margins.ContainsKey( shape))
							_margins.Add( shape, GetMargins( shape ));
						else
							_margins[shape] = GetMargins( shape);
					}
				}

				RefreshResizeTransformBase();
				ResetPath( false );
				OnScalingResizeChanged( new EventArgs());
			}
		}


		private bool _boundsChanging;
		public override RectangleF Bounds
		{
			get	{	return base.Bounds;	}
			set
			{
				bool alreadySuspended = base.Suspended;

				try
				{
					_boundsChanging = true;

					if(!alreadySuspended)  this.SuspendLayout();
					RectangleF oldBounds = base.Bounds;
			
					base.Bounds = value;
					_lockedBounds.Location = base.Position;
					if( !_scalingResize ) _lockedBounds.Size = base.Size;//NEW...01.09.06

					RefreshResizeTransformBase();

					if( _initializing ) return;
			

					RectangleF newBounds = base.Bounds;
					if( newBounds == oldBounds) return;

					CleanContainerBaseShape();

					ResetLayout( oldBounds, newBounds );
					if( this.CanWarp && this.Warp != Warp.Empty ) RewarpChildren(null);
				}
				finally
				{
					_boundsChanging = false;
					if(!alreadySuspended) this.ResumeLayout();
				}


			}
		}



		[Browsable( false)]
		[DefaultValue(null)]
		[DesignerSerializationVisibility( DesignerSerializationVisibility.Hidden)]
		public override ShapeCombine Combine
		{
			get{ return ShapeCombine.Empty; }
			set
			{ 
				throw new InvalidOperationException("Shape Container can not be joined with another shape");
			}
		}

		[Browsable( false)]
		public override bool ParentExclude
		{
			get
			{
				return false;
			}
			set
			{
				throw new InvalidOperationException( "Shape container can not be combined with any other shape type");
			}
		}





		#endregion Properties

		#region HIT TEST OVERRIDES


		private bool IsShapeDesignMode(Shape shape)
		{
			if( shape.Site == null) return false;
			return shape.Site.DesignMode;
		}

		private int VisibleCount
		{
			get
			{

				int i=0;
				foreach( Shape shape in _shapes )
				{
					if( IsShapeDesignMode( shape)&&!shape.ParentExclude ) i++;//neu
					else if( shape.Visible &&!shape.ParentExclude ) i++;
				}

				return i;
			}
				
		}



		private PointF InvertScalingResizePoint(PointF point)
		{
			using(Matrix m = this.ResizeTransform)
			{
				if( m.IsIdentity) return point;

				m.Invert();
				PointF[] pts = new PointF[] { point };
				m.TransformPoints(pts);
				return pts[0];
			}

		}

		private bool IsExcludeHit( PointF point)
		{
			
			foreach( Shape shape in _shapes )	
			{
				if( !shape.Visible ) continue;
				if( shape.ParentExclude)
					if( shape.Path.IsVisible( point )) return true;
			}

			return false;
		}

		public override bool HitTest(PointF point)
		{
			return this.HitTest (point, true);
		}

		internal override bool HitTest( PointF point, bool invertScalingResize )
		{
			if( !this.Visible && !this.DesignMode)	return false;	
			int visibleCount = this.VisibleCount;

			//Invert globally
			if( invertScalingResize) point = base.ScalingResizeTransformPoint( point, true); 
			
			//Invert again locally
			PointF ptInvert = this.InvertScalingResizePoint( point );

			bool clipHit = _clip && _clippingRegion.IsVisible(  point );
			if(_clip && !clipHit ) return false;

			if( _excludePath != null)
			{
				bool excludeHit =   IsExcludeHit(ptInvert);
				if( excludeHit ) 
				{

					return false;

//					if( _excludeGlobal ) return false;
//					else
//					{
//						if( visibleCount == 0 ) return true;
// 
//
//						foreach( Shape shape in this._shapes ) 
//						{
//							if( shape.ParentExclude ) continue;
//							if( shape.HitTest( ptInvert , false )) return true;
//						}
//
//						return false;
//					}
				}
			}
			
			if( clipHit && !_paintChildsOnly) return true;

			
			if(_excludePath != null && !_paintChildsOnly )
			{
				Pen excludeHitPen = this.ExcludeHitPen;
				if( _excludePath.IsOutlineVisible(point, excludeHitPen )) return true;
			}

			bool thisHit = _paintChildsOnly ? false: base.HitTest( point , false);

			if( visibleCount == 0) return thisHit;


			foreach( Shape shape in this._shapes ) 
			{
				if( shape.ParentExclude ) continue;
				if( shape.HitTest(ptInvert, false )) return true;
			}

			return thisHit;
				
		}




		
		#endregion HIT TEST OVERRIDES


		public override void ResetTransform(  )
		{
			bool alreadySuspended = base.Suspended;

			try
			{

				if( !alreadySuspended ) this.SuspendLayout();
				this.ScalingResize	= false;
				base.ResetTransform(  );

			}
			finally
			{
				if( !alreadySuspended ) 	this.ResumeLayout();

			}
		}
	

		public override void SuspendLayout()
		{
			if( base.Suspended ) return;

			foreach( Shape shape in _shapes) shape.SuspendLayout();
			base.SuspendLayout ();
		}



		public override void ResumeLayout()
		{

			foreach( Shape shape in _shapes) shape.ResumeLayout( );
			base.ResumeLayout ();
		}


		protected RectangleMargins GetMargins( Shape  shape )
		{
			RectangleF r = ShouldScaleOnResize ? _lockedBounds :this.Bounds;
			return new RectangleMargins(r,shape.Bounds);
		}



		protected override void OnWarpChanging(ValueChangingEventArgs eventArg)
		{
			SetCanWarp( !_scalingResize );
			if( _scalingResize ) eventArg.Cancel = true;

			base.OnWarpChanging (eventArg);
		}

		protected override void OnWarpChanged(ValueChangeEventArgs eventArg)
		{
			base.OnWarpChanged (eventArg);
			if( _rewarpingChildren ) return;
			RewarpChildren(null);
		}

		protected internal override QuickWarper ParentWarper
		{
			get
			{
				return base.ParentWarper;
			}
			set
			{
				base.ParentWarper = value;
				RewarpChildren(null);
			}
		}

		bool _rewarpingChildren = false;
		private bool RewarpChildren( Shape child )
		{	//Returns true if and rewarped

			if( !base.CanWarp) return false;
			if( _rewarpingChildren ) return true;

			_rewarpingChildren = true;
			PointF[] pts = null;

			try
			{
				RectangleF bounds = base.Bounds;//Old

				//RewarpChildren
				//----------------------------------------------------------------------------------
				foreach( Shape shape in this._shapes )	shape.ParentWarper = null;

				
				if( base.CanWarp && !base.Warp.IsEmpty) pts = this.Warp.GetPoints( bounds );

				if( this.ParentWarper != null )
				{
					if( pts == null )	pts = base.Warp.GetPoints(bounds);
					this.ParentWarper.WarpPoints( pts);
				}
				
				if( pts != null  )
				{
					QuickWarper		warper = new QuickWarper( pts, bounds, base.Warp.WarpMode);
					foreach( Shape shape in this._shapes )	 
					{
						if( child == null) shape.ParentWarper = warper;
						else if( shape == child) 
						{
							shape.ParentWarper = warper;
							shape.ResetPath( false );
						}
					}
				}

				return pts != null;

			}
			finally
			{

				if( child == null)
				{
					foreach( Shape shape in this._shapes )	if( shape != child) shape.ResetPath( false );
					this.ResetPath( false );
				}

				_rewarpingChildren = false;
			}

		}

		private bool _layoutResetting = false;
		private void ResetLayout( RectangleF oldValue, RectangleF newValue)
		{
			if( _layoutResetting ) return;

			_layoutResetting = true;
			try		{	foreach( Shape shape in _shapes)	ResetLayout( oldValue, newValue, shape);	}
			finally	{_layoutResetting = false;	}
			
		}

		private void ResetLayout( RectangleF oldValue, RectangleF newValue, Shape shape)
		{
				if( shape is VectorShape) 
				{
					((VectorShape)shape).RecalculatePoints( Rectangle.Round( oldValue ), Rectangle.Round(newValue));
					this.ResetPath();
					return;
				}
			
				//--------------------------------------------------------------------
				//Should Only be called in EndInit Method
				//--------------------------------------------------------------------
				PointF	p1  = oldValue.Location;
				PointF	p2  = newValue.Location;
				PointF  dl	= new PointF( p2.X - p1.X, p2.Y - p1.Y);
				SizeF	ds	= newValue.Size - oldValue.Size;
	
	
				if( shape.Anchor == AnchorStyles.None) return;

				RectangleMargins margins = (RectangleMargins)_margins[shape];

				int anchorStyle = (int)shape.Anchor;
				RectangleF	bounds	= shape.Bounds;

	
				if( (anchorStyle & (int)AnchorStyles.Left)	> 0 && (anchorStyle & (int)AnchorStyles.Right) > 0 )
				{
					if( ShouldScaleOnResize) bounds.X	+= dl.X;//New 10.08.2005:Suppose only left
					else
					{
						bounds.X		+= dl.X;
						bounds.Width	+= ds.Width;
	
						if( shape.Bounds.Size.Width == shape.MinSize.Width)
							if( newValue.Right > oldValue.Right)
								bounds.Width = newValue.Right - bounds.X - margins.Right;


						if( bounds.Right > newValue.Right - margins.Right)
							bounds.Width = newValue.Right - bounds.X - margins.Right;
					}
				}
				else
				{
					if( (anchorStyle & (int)AnchorStyles.Left)   > 0)	bounds.X	+= dl.X;

					if( (anchorStyle & (int)AnchorStyles.Right)  > 0 )
						bounds.X	= newValue.Right - margins.Right - bounds.Width;

				}				


				if( (anchorStyle & (int)AnchorStyles.Top)	 > 0 && (anchorStyle & (int)AnchorStyles.Bottom) > 0 )
				{
					if( ShouldScaleOnResize) bounds.Y		+= dl.Y;//New 10.08.2005:Suppose only Top
					else
					{
						bounds.Y +=dl.Y;
						bounds.Height += ds.Height;

						if( shape.Bounds.Size.Height == shape.MinSize.Height)
							if( newValue.Bottom > oldValue.Bottom)
								bounds.Height = newValue.Bottom - bounds.Y - margins.Bottom;

						if( bounds.Bottom > newValue.Bottom - margins.Bottom)
							bounds.Height = newValue.Bottom - bounds.Y - margins.Bottom ;
					}
				}
				else
				{
					if( (anchorStyle & (int)AnchorStyles.Top)    > 0 )	bounds.Y		+= dl.Y;

					if( (anchorStyle & (int)AnchorStyles.Bottom) > 0 )	
						bounds.Y	= newValue.Bottom - margins.Bottom - bounds.Height;
				}

			if( bounds != shape.Bounds ) 	
			{
				shape.Bounds = bounds;
//				if( _margins.ContainsKey(shape) )//new: 09.02.2007
//				_margins[ shape ] = GetMargins( shape ); // Read always again !!!!

			}
			
				

				base.ResetPath();



		}


		[Category("Shapes")]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
		public ShapeCollection Shapes
		{
			get
			{
				return _shapes;
			}
		}


		#region Add/Remove shape Events
		private void AddShapeEvents( Shape shape )
		{
			if( shape == null) return;
			shape.BoundsChanged			+= new ValueChangeEventHandler(Shape_BoundsChanged);//Non-Override version
			shape.BoundsChanged			+= new ValueChangeEventHandler(OnShapeBoundsChanged);
			shape.InternalChanged		+= new EventHandler(OnShapeInternalChanged);
			shape.PathBoundsChanged		+= new ValueChangeEventHandler(OnShapePathBoundsChanged);
			shape.ParentExcluded		+= new EventHandler(OnShapeExcluded);
		}

		private void RemoveShapeEvents( Shape shape )
		{
			if( shape == null) return;

			shape.BoundsChanged			-= new ValueChangeEventHandler(Shape_BoundsChanged);//Non-Override version
			shape.BoundsChanged			-= new ValueChangeEventHandler(OnShapeBoundsChanged);
			shape.InternalChanged		-= new EventHandler(OnShapeInternalChanged);
			shape.PathBoundsChanged		-= new ValueChangeEventHandler(OnShapePathBoundsChanged);
			shape.ParentExcluded		-= new EventHandler(OnShapeExcluded);
		}
		#endregion Add/Remove shape Events

		#region Shape Events

		[Browsable(false), Category( "Misc(Children)")]
		public event EventHandler				ShapeInternalChanged	= null;

		[Browsable(true), Category( "Layout(Children)")]
		public event ValueChangeEventHandler	ShapeBoundsChanged		= null;

		[Browsable(true), Category( "Layout(Children)")]
		public event ValueChangeEventHandler	ShapePathBoundsChanged	= null;

		[Category("Region(Children)")]
		public event EventHandler				ShapeExcluded	= null;

		#endregion Shape Events

		#region Event Invokers

		protected virtual void OnShapeInternalChanged(object sender, EventArgs e)
		{	
			if( ShapeInternalChanged != null) ShapeInternalChanged(sender,e);
		}

		protected virtual void OnShapeBoundsChanged(object sender, ValueChangeEventArgs e)
		{
			if( this.ShapeBoundsChanged!=null) this.ShapeBoundsChanged(sender,e);

		}

		protected virtual void OnShapePathBoundsChanged( object sender, ValueChangeEventArgs e)
		{
			if( this.ShapePathBoundsChanged !=null ) this.ShapePathBoundsChanged(sender,e);

			if(_initializing || _painting ||_layoutResetting ||  _rewarpingChildren )  return;


			Shape shape = (Shape)sender;
		
		
			if( shape.ParentExclude )
			{
				RefreshExcludePath();
				InvokeInternalChanged( false );
			}
		
		}


		protected virtual void OnShapeExcluded(object sender, EventArgs eventArg)
		{
			try
			{
				RefreshExcludePath();
				InvokeInternalChanged( false );

				if( this.ShapeExcluded != null) this.ShapeExcluded( sender, eventArg );
			}
			catch( Exception exc)
			{
				FileWatcher.WriteLine("ShapeContainer::OnShapeExcluded," + exc.StackTrace);
			}


		}


		// This event handler should not be overridden
		private void Shape_BoundsChanged(object sender, ValueChangeEventArgs e)
		{ 
			if( _initializing ) return;

			Shape shape = (Shape)sender;

			RewarpChildren(shape);

			RectangleMargins oldMargin = (RectangleMargins)_margins[shape];
			RectangleMargins newMargin = GetMargins( shape);//e.NewValue );

			if( shape.Size.Height == shape.MinSize.Height)
			{
				newMargin.Top			= oldMargin.Top;
				newMargin.Bottom		= oldMargin.Bottom;
			}

			if( shape.Size.Width == shape.MinSize.Width)
			{
				newMargin.Left = oldMargin.Left;
				newMargin.Right = oldMargin.Right;
			}

			_margins[sender]= newMargin;

		}

		#endregion Event Invokers



		private void OnShapeCollectionChanged(object sender, ShapeCollectionEventArgs e)
		{
			

			switch( e.Action )
			{
				case ShapeCollectionEventArgs.CollectionAction.Clear:
					foreach( Shape shape in _shapes) 
						if( shape != null)	 
						{  RemoveShapeEvents( shape );	shape.Parent = null; }
					break;
				case ShapeCollectionEventArgs.CollectionAction.Insert:
					if( e.NewValue != null ) 
					{	
						Shape shape = e.NewValue as Shape;
						AddShapeEvents( shape );
						shape.Parent = this;	
					}
					break;
				case ShapeCollectionEventArgs.CollectionAction.Remove:
					if( e.OldValue != null ) 
					{
						Shape shape = e.OldValue as Shape;
						RemoveShapeEvents( shape);	
						shape.Parent = null;	
					}
					break;
				case ShapeCollectionEventArgs.CollectionAction.Set:
					if( e.OldValue != null ) 
					{ 
						Shape shape = e.OldValue as Shape;
						RemoveShapeEvents( shape);	
						shape.Parent = null;	
					}
					if( e.NewValue != null ) 
					{ 
						Shape shape = e.NewValue as Shape;
						AddShapeEvents( shape );	
						shape.Parent = this;	
					}
					break;

				default:
					break;
			}

			if( _initializing ) return;

			switch( e.Action )
			{
				case ShapeCollectionEventArgs.CollectionAction.Clear:
					_margins.Clear();
					break;

				case ShapeCollectionEventArgs.CollectionAction.Insert:
					_margins.Add( e.NewValue, GetMargins( e.NewValue as Shape));
					break;

				case ShapeCollectionEventArgs.CollectionAction.Remove:
					_margins.Remove( e.OldValue as Shape);
					break;
					
				case ShapeCollectionEventArgs.CollectionAction.Set:

					if( this.Shapes.Contains(e.OldValue as Shape))
						_margins[e.OldValue] = GetMargins(e.OldValue as Shape);
					else 
						_margins.Remove(e.OldValue);

					if( this.Shapes.Contains(e.NewValue as Shape))
						_margins[e.NewValue] = GetMargins(e.NewValue as Shape);
					else
						_margins.Add(e.NewValue, GetMargins( e.NewValue as Shape));

					break;
				default:
					break;
			}

		}

		[DefaultValue(typeof(Size),"50,50")]
		public override Size MinSize
		{
			get
			{
				return base.MinSize;
			}
			set
			{
				base.MinSize = value;
			}
		}


		#region PAINTING




		private Shape _containerBaseShape = null;



		[DefaultValue( null)]
		[Category("Layout")]
		public Shape ContainerBaseShape
		{
			get
			{
				CleanContainerBaseShape();
				return _containerBaseShape;
			}
			set
			{
				if( _containerBaseShape == value) return;

			
				if( value is ShapeContainer) 
					throw new InvalidOperationException("A Shape Container can not be used as base path shape");

				if(_containerBaseShape != null)
				{
					_containerBaseShape.PathBoundsChanged-=new ValueChangeEventHandler(ContainerBaseShape_PathBoundsChanged);
					_containerBaseShape.Parent = null;
					_containerBaseShape.IsContainerBaseShape = false;

					if( this.DesignMode)
					{
						_containerBaseShape.SetCanResize(true);
						_containerBaseShape.Visible = true;
						IDesignerHost host = (IDesignerHost) this.Site.GetService(typeof(IDesignerHost));
						IShapeContainerProvider rootComponent = host.RootComponent as IShapeContainerProvider;
						if( rootComponent != null)
							rootComponent.ShapeContainer.Shapes.Add( _containerBaseShape );
						
					}
					
				}

				_containerBaseShape = value;

				if( _containerBaseShape != null)
					_containerBaseShape.PathBoundsChanged+=new ValueChangeEventHandler(ContainerBaseShape_PathBoundsChanged);

				CleanContainerBaseShape();
				this.InvokeInternalChanged( true );

			}
		}
		

		private void ContainerBaseShape_PathBoundsChanged(object sender, ValueChangeEventArgs e)
		{
			this.InvokeInternalChanged( true );
		}


		protected override void OnPathBoundsChanged(ValueChangeEventArgs eventArg)
		{
			if( !_boundsChanging) RefreshResizeTransform();

			if( _clippingRegion != null) _clippingRegion.Dispose();
			_clippingRegion = null;


			if( _clip ) _clippingRegion = GetClippingRegion();


			RefreshExcludePath();
			
			base.OnPathBoundsChanged (eventArg);

		}



		private bool _paintChildsOnly;

		[Browsable( true )]
		[Category("Region")]
		[DefaultValue( false )]
		[RefreshProperties( RefreshProperties.Repaint)]
		public bool PaintChildsOnly
		{
			get { return _paintChildsOnly;}
			set 
			{ 
				if( value == _paintChildsOnly) return;

				_paintChildsOnly = value;

//				if( _paintChildsOnly) _excludeGlobal = true;

				InvokeInternalChanged( true );
			}

		}

//		[Category("Region")]
//		[Browsable(true)]
//		[DefaultValue(true)]
//		public bool ExcludeGlobal
//		{
//			get{ return _excludeGlobal;}
//			set
//			{
//				if( _paintChildsOnly || _clip) value = true;
//
//				if( _excludeGlobal == value) return;
//
//				_excludeGlobal = value;
//				
//				InvokeInternalChanged(false);
//			}
//
//		}


		/// <summary>
		/// This container might be clipped externally which requires a clipped draw...
		/// </summary>
		internal bool PaintClipped;

		[Browsable( true )]
		[Category("Region")]
		[DefaultValue( false )]
		[RefreshProperties( RefreshProperties.Repaint)]
		public bool Clip
		{
			get{ return _clip;}
			set
			{
				if( _clip == value) return;

				_clip = value;
				
//				if( _clip ) _excludeGlobal = true;
				
				InvokeInternalChanged(true);
				this.OnClipChanged( new EventArgs());
			}
		}

	

		private Region _clippingRegion = null;
		private Region GetClippingRegion()
		{
			return new Region( this.Path );
		}


		public override Region GetRegion(bool transformScalingResize)
		{
			try
			{
				Region r = null;
				Matrix parentTransform = this.ScalingResizeTransform( false );
				if( parentTransform.IsIdentity) {parentTransform.Dispose(); parentTransform = null;}

				Matrix m = null;

				if( transformScalingResize )
				{
					m = this.ResizeTransform;
					if( parentTransform != null ) m.Multiply( parentTransform, MatrixOrder.Append );
					if( m.IsIdentity ) { m.Dispose(); m = null;}
				}

				using( m )using(parentTransform)
				{
					bool clipped = _clip && _clippingRegion != null;

					if( _paintChildsOnly )
					{
						foreach( Shape shape in this.Shapes)
						{
							if( shape.ParentExclude) continue;

							if( r == null) r = new Region();
							r.Exclude( shape.Path );
							if( shape.TextPath != null) r.Exclude( shape.TextPath );
						}

						if( r != null  )
						{
							r.Complement( new Region() );
							if( m != null) r.Transform( m );
							
							if( clipped ) 
								using(Region rc = _clippingRegion.Clone())
								{
									if( parentTransform != null) rc.Transform( parentTransform);

									rc.Complement( new Region());
									r.Exclude( rc );
								}
						}
					} 
					else if( clipped ) 
					{
						r = _clippingRegion.Clone();
						if( parentTransform != null) r.Transform( parentTransform);
					}


					if( r == null) 
					{
						r = GetClippingRegion();
						if( parentTransform != null) r.Transform( parentTransform);
					}

					if( _excludePath != null)
						foreach( Shape shape in this.Shapes)
						{
							if( !shape.ParentExclude) continue;

							using(Region reg = new Region( shape.Path ))
							{
								if( m != null) reg.Transform( m );
								r.Exclude( reg );
							}
						}


					return r;
				}
			}
			catch( Exception e )
			{
				FileWatcher.WriteLine("ShapeContainer::GetRegion failed...\n" +  e.Message + "\n" + e.StackTrace );
				return null;
			}
		}




		protected override GraphicsPath MakeTextPath(RectangleF baseBounds)
		{
//			if( this.ContainerBaseShape != null ) return base.MakeTextPath( baseBounds );

			if( _resizeTransformBase == null) return base.MakeTextPath( this.Bounds);// baseBounds );

			GraphicsPath textPath = base.MakeTextPath ( _lockedBounds );


			if( textPath == null ) return null;

			using(Matrix  m = this.ResizeTransformBase)
			{
				if( m == null || m.IsIdentity ) return textPath;

				textPath.Transform( m );
			}

			return textPath;
		}

		protected override GraphicsPath MakePath(RectangleF bounds)
		{
			GraphicsPath path = new GraphicsPath();

			if(_containerBaseShape != null) 
			{
				CleanContainerBaseShape();

				path = _containerBaseShape.ClonePath();

				// New... Also in CleanContainerBaseShape method the 
				// "base.GlobalTransform = this.ResizeTransformBase" removed 

				using(Matrix  m = this.ResizeTransformBase ) if( m != null ) path.Transform( m );

			}
			else
			{

				path = new GraphicsPath();
				path.AddRectangle( bounds );
			}
		
			return path;
		}

		private void CleanContainerBaseShape()
		{
			try
			{
				if( _containerBaseShape != null)
				{
					if( _containerBaseShape.Parent != null && _containerBaseShape.Parent.Shapes.Contains( _containerBaseShape ))
						_containerBaseShape.Parent.Shapes.Remove( _containerBaseShape );

					_containerBaseShape.IsContainerBaseShape = true;
					_containerBaseShape.Parent = this;
					_containerBaseShape.SetCanResize( true );

					RectangleF bounds = this.ShouldScaleOnResize ? _lockedBounds : this.Bounds;
					bounds.Location = this.Bounds.Location;
					_containerBaseShape.Bounds = bounds;
//					base.GlobalTransform = this.ResizeTransformBase;

					_containerBaseShape.Visible = false;
					_containerBaseShape.SetCanResize( false );
				}
//				else base.GlobalTransform = null;
			}
			catch( Exception e )
			{
				FileWatcher.WriteLine("ShapeContainer::CleanContainerBaseShape\n," + e.StackTrace );

			}
		}


		#region ResizeTransform

		private Matrix _resizeTransformBase = null;
		private Matrix _resizeTransform		= null;


		private void RefreshResizeTransform()
		{
			if( _resizeTransform != null) _resizeTransform.Dispose();
			
			if( _resizeTransformBase == null) _resizeTransform = base.Matrix.Clone();
			else
			{
				_resizeTransform = _resizeTransformBase.Clone();
				_resizeTransform.Multiply( base.Matrix, MatrixOrder.Append);
			}
		}

		private void RefreshResizeTransformBase()
		{
			if( _resizeTransformBase != null) _resizeTransformBase.Dispose();
			
			_resizeTransformBase = null;

			_resizeTransformBase = MakeResizeTransformBase();

			RefreshResizeTransform();
		}

		private Matrix MakeResizeTransformBase()
		{
			if( !ShouldScaleOnResize ) return null;

			Matrix m = null;

			RectangleF bounds	= this.Bounds;
			float	scaleX = bounds.Width/_lockedBounds.Width;
			float	scaleY = bounds.Height/_lockedBounds.Height;

			RectangleF scalingBase = _lockedBounds;
			scalingBase.Location = bounds.Location;

			if( scaleX != 1f || scaleY != 1f )
			{
				if(m == null) m = new Matrix();
				using(GraphicsPath path = new GraphicsPath())
				{
					path.AddRectangle( scalingBase );
					m.Scale(scaleX, scaleY, MatrixOrder.Append);
					RectangleF pathBounds = path.GetBounds( m );

					PointF p1			= pathBounds.Location;
					PointF p2			= bounds.Location;

					if( p1 != p2)
					{
						float dx = p2.X - p1.X;
						float dy = p2.Y - p1.Y;
						m.Translate( dx,dy, MatrixOrder.Append );
					}
				}
			}
			return m;


		}


		[Browsable( false)]
		public Matrix ResizeTransform
		{

			get
			{

				if( this._resizeTransform == null) RefreshResizeTransform();
				return _resizeTransform.Clone();

				//				Matrix m = this.ResizeTransformBase;
				//
				//				if( m == null) m = base.Matrix.Clone();
				//				else m.Multiply( base.Matrix, MatrixOrder.Append);
				//				return m;
				
			}
		}


		[Browsable( false)]
		internal Matrix ResizeTransformBase
		{
			get
			{
				if( _resizeTransformBase != null) return _resizeTransformBase.Clone();
				return null;


				//				return MakeResizeTransformBase();
			}
		}


		#endregion ResizeTransform
		
		public override RectangleF InvalidatingBounds
		{
			get
			{
				bool clippedPaint = _clip||this.PaintClipped;
				if( clippedPaint) return this.GetPathBounds();

				RectangleF bounds = base.InvalidatingBounds;
				
				if( _shapes.Count == 0) return bounds;
				
				int calculateFrom = 0;
				if( _paintChildsOnly)
				{
					bounds = _shapes[0].InvalidatingBounds;
					calculateFrom = 1;
				}

				for( int i = calculateFrom; i<_shapes.Count; i++)
				{
					Shape shape = _shapes[i];
					bounds = RectangleF.Union( bounds, shape.InvalidatingBounds);
				}

				return bounds;

			}
		}


		private Pen _hitPen				= null;
		internal override Pen HitPen
		{
			get	
			{
				return ( _hitPen == null ? base.DefaultHitPen:_hitPen );
			}
		}
		
		private Pen _exludeHitPen = null;
		internal Pen ExcludeHitPen
		{
			get	
			{
				return (_exludeHitPen == null ? base.DefaultHitPen:_exludeHitPen );
			}
		}

		protected internal override Pen GetPen(ShapePen shapePen, bool transform, bool text)
		{
			Pen pen = base.GetPen (shapePen, transform, text);
			
			if( pen == null) return null;

			if( !transform) return pen;



			Matrix m = pen.Transform;
			if( m != null ) 
			{
				using( Matrix rt = this.ResizeTransform)
					m.Multiply( rt );
				
				//m.Multiply( this.ResizeTransform );
				pen.Transform = m;
			}
			else
				pen.Transform = this.ResizeTransform;
			
			return pen;
		}


		protected internal override Pen GetPen(ShapePen shapePen, RectangleF bounds, bool transform)
		{
			Pen pen = base.GetPen (shapePen, bounds, transform);
			if( pen == null) return null;

			if( !transform ) return pen;

			Matrix m = pen.Transform;
			if( m != null ) 
			{
				using( Matrix rt = this.ResizeTransform)
				{
					//m.Multiply( this.ResizeTransform );
					m.Multiply( rt );
					pen.Transform = m;
				}
			}
			else
				pen.Transform = this.ResizeTransform;

			return pen;
		}



		GraphicsPath _excludePath = null;
		private void RefreshExcludePath()
		{
//			_excludePath = this.GetExcludePath( true );

			if( _excludePath != null) _excludePath.Dispose();
			
			_excludePath = null;

			foreach( Shape shape in _shapes )	
			{
				if( !shape.Visible ) continue;

				if( shape.ParentExclude)
				{	
					if( _excludePath == null) _excludePath = shape.ClonePath();
					else _excludePath.AddPath( shape.Path, true );
				}
			}

			if(_excludePath == null ) return;

			//if( resizeTransform ) 
			using( Matrix rt = this.ResizeTransform) _excludePath.Transform( rt );

		}


		private bool _painting = false;
		public override void Paint( Graphics g )
		{
			
			if( _painting || base.Suspended )	return;
			if( !this.Visible && !this.DesignMode )	return;	

			_painting = true;

			GraphicsContainer container = g.BeginContainer();
			

			try
			{

				GraphicsPath path =  base.Path;
				if( path == null) return;

				path.FillMode = base.FillMode;

				g.SmoothingMode = SmoothingMode.AntiAlias;
				
				if( _clip ) g.Clip = _clippingRegion;
				
				if( _excludePath != null ) 
					using(Matrix rt = this.ResizeTransform)
						foreach( Shape shape in _shapes )	
							if( shape.ParentExclude)
								using(Region r = new Region( shape.Path ))
								{
									r.Transform( rt );
									g.ExcludeClip( r );
								}


				Pen	pen = null;
				_hitPen = _exludeHitPen = null;
				
				bool clippedPaint = _clip||this.PaintClipped;

				if( base.Pen != null)
				{
					if( clippedPaint || _excludePath == null )	pen = this.GetPen( base.Pen, base.TransformPen );
					else pen = this.GetPen( base.Pen,RectangleF.Union( _excludePath.GetBounds(), base.GetPathBounds()), base.TransformPen);
				}

				if( pen != null) _hitPen = pen.Clone() as Pen;
				if( _excludePath != null) _exludeHitPen = _hitPen;

				Pen shadePen = null;
				Brush shadeBrush = null;
				Brush shadeBackgBrush = null;

				#region ---------------------------------------- Draw Shade ---------------------------------------------
				
				bool canPaintShade = base.Shade != null && ( base.Shade.Pen != null || base.Shade.Fill != null);
				
				// Initialize Shade pen and brush
				//----------------------------------
				if( canPaintShade )
				{
					if( _excludePath != null )
					{
						if( base.Shade.Pen != null)//Initialize shade pen..We need it later also..
							shadePen = this.GetPen( base.Shade.Pen,RectangleF.Union( _excludePath.GetBounds(), base.GetPathBounds()), base.TransformPen);

						if(base.Shade.Fill != null)//Draw Exclude-Shade backg.
						{
							
							if(base.Shade.Fill.FillType == FillType.PathGradient && 
								((PathGradientFill)base.Shade.Fill).FillMode == PathGradientFillMode.Default)
								shadeBrush = ((PathGradientFill)base.Shade.Fill).GetBrush( RectangleF.Union( _excludePath.GetBounds(), base.GetPathBounds()) );
							else 
							{
								//Get shade background brush
								shadeBrush = base.GetBrush( base.Shade.Fill, RectangleF.Union( _excludePath.GetBounds(), base.GetPathBounds()), true);
							}
						}
					}
					else if (!_paintChildsOnly && !clippedPaint)
					{
						if( base.Shade.Pen != null)//Initialize shade pen..We need it later also..
							shadePen = this.GetPen( base.Shade.Pen, base.TransformPen );

						if(base.Shade.Fill != null)//Draw Exclude-Shade backg.
							shadeBrush = base.GetBrush( base.Shade.Fill, true );
					}
				}
				
				// Paint shadow of the container
				//---------------------------------
				if( canPaintShade && !clippedPaint && !_paintChildsOnly ) //Do not paint shadow when clipped or only childs to be painted
				{
					if( shadePen == null && this.Shade.Fill == null && this.Site != null) base.DrawInvisible(g, path); 
					else//Draw shade of this container
					{
						base.DrawShade( 
							g, 
							path, 
							base.Shade.Offset, 
							base.Shade.Repeat, 
							shadeBrush,
							shadePen, 
							base.Shade.Pen != null && base.Shade.Pen.ForegroundPen, 
							true );

					}
				}
				#endregion Draw Shade

				#region -----------------------------  Draw background and border  --------------------------------------
 
				if( !_paintChildsOnly )
				{
					if( pen == null && base.Fill == null && this.Site != null) base.DrawInvisible(g, path);
					else
					{
						if( clippedPaint )//If clip set paint only the backg. and draw the the path later.. 
							base.DrawBackground(g, base.Fill , false );
						else
						{
							if( pen != null && !base.Pen.ForegroundPen) g.DrawPath( pen, this.Path);
							base.DrawBackground(g, base.Fill , false );
							if( pen != null && base.Pen.ForegroundPen) g.DrawPath(pen, this.Path);

						}
					}

					this.DrawText(g);
				}

				using(Matrix resizeTransform = this.ResizeTransform)
				{
					g.Transform = resizeTransform;

					//Paint Shapes that are not excluded
					foreach( Shape shape in this._shapes )	
						if ( !shape.ParentExclude ) shape.Paint(g);

					g.ResetTransform();
				}
				

				if( canPaintShade && _excludePath != null )
				{
					//Draw Exclude Shade
					//-----------------------------------------------------------------------------
					base.DrawShade( g, 
						_excludePath, 
						base.Shade.Offset,
						base.Shade.Repeat,
						shadeBrush,
						shadePen,
						base.Shade.Pen != null && base.Shade.Pen.ForegroundPen,
						true);
				}


				if( pen != null  ) 
				{
					//Do not draw container path  clipped
					if(clippedPaint && !_paintChildsOnly ) g.DrawPath(pen, this.Path);

					//But draw the excluded region
					if( _excludePath != null)  
					{
						//Draw  this container
						if( this.Pen.ForegroundPen && !clippedPaint ) { pen.Width*=2f;_exludeHitPen = pen.Clone() as Pen;}
						g.DrawPath( pen, _excludePath);
					}
				}




				g.ResetClip();


				#endregion Draw Background and border

				if( shadePen != null) shadePen.Dispose(); shadePen = null;
				if( shadeBrush != null) shadeBrush.Dispose(); shadeBrush = null;
				if( shadeBackgBrush != null) shadeBackgBrush.Dispose(); shadeBackgBrush = null;
				if( pen != null) pen.Dispose(); pen = null;

			}
			catch(Exception e )
			{
				g.ResetClip();
				g.FillPath( Brushes.Black, base.BasePath);
				g.DrawString(e.Message + "\n" + e.StackTrace,new Font("Tahoma", 10),Brushes.White,base.BasePath.GetBounds());
			}
			finally
			{
				
				g.ResetClip();
				if( container != null) g.EndContainer(container);
				_painting = false;
			}
		}


	
		private void DrawText( Graphics g)
		{
			if( _paintChildsOnly) return;

			GraphicsContainer c = g.BeginContainer();
			g.SmoothingMode = SmoothingMode.AntiAlias;
			#region ---------------------------- DRAW TEXT  ---------------------------------------------------------

			GraphicsPath textPath = base.TextPath;
			if( textPath == null) return;
			textPath.FillMode = FillMode.Winding;

			//Draw Text Shade
			//--------------------------------------------------------------------------------------------------------------
			if( base.TextShade != null )
			{
				g.SetClip(textPath, CombineMode.Exclude);
				base.DrawShade( g, true);
				g.ResetClip();

			}

			//Draw TEXT Background and border
			//--------------------------------------------------------------------------------------------------------------
			if( base.TextPen != null && !base.TextPen.ForegroundPen) base.DrawBorder(g, base.TextPen, base.TransformTextPen, true);
			bool bgOK = base.DrawBackground(g, base.TextFill , true );
			if( base.TextPen != null && base.TextPen.ForegroundPen) base.DrawBorder(g, base.TextPen, base.TransformTextPen, true);


			if( base.TextPen == null && bgOK )
			{
				ShapePen antiAliasPen = new ShapePen( base.TextFill );
				base.DrawBorder( g, antiAliasPen, true, true );
			}

			#endregion DRAW TEXT

			g.EndContainer( c );
		}

		#endregion PAINTING



	}
}
